/* proto.c
 * Routines for protocol tree
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#define SUBTREE_ONCE_ALLOCATION_NUMBER 8
#define SUBTREE_MAX_LEVELS 256

#define max_tree_items 32

#define cVALS(x) (const value_string*)(x)

/** See inlined comments.
 @param tree the tree to append this item to
 @param free_block a code block to call to free resources if this returns
 @return NULL if 'tree' is null */
#define CHECK_FOR_NULL_TREE_AND_FREE(tree, free_block)			\
	if (!tree) {							\
		free_block;						\
		return NULL;						\
	}

/** See inlined comments.
 @param tree the tree to append this item to
 @param free_block a code block to call to free resources if this returns
 @return NULL if 'tree' is null */
#define CHECK_FOR_NULL_TREE(tree) \
	CHECK_FOR_NULL_TREE_AND_FREE(tree, ((void)0))

/** See inlined comments.
 @param length the length of this item
 @param cleanup_block a code block to call to free resources if this returns
 @return NULL if 'length' is lower -1 or equal 0 */
#define CHECK_FOR_ZERO_OR_MINUS_LENGTH_AND_CLEANUP(length, cleanup_block)	\
	if (length < -1 || length == 0 ) {				\
		cleanup_block;						\
		return NULL;						\
	}

/** See inlined comments.
 @param length the length of this item
 @return NULL if 'length' is lower -1 or equal 0 */
#define CHECK_FOR_ZERO_OR_MINUS_LENGTH(length) \
	CHECK_FOR_ZERO_OR_MINUS_LENGTH_AND_CLEANUP(length, ((void)0))

/** See inlined comments.
 @param tree the tree to append this item to
 @param hfindex field index
 @param hfinfo header_field
 @param free_block a code block to call to free resources if this returns
 @return the header field matching 'hfinfo' */
#define TRY_TO_FAKE_THIS_ITEM_OR_FREE(tree, hfindex, hfinfo, free_block) \
    /* If the tree is not visible and this item is not referenced	\
       we don't have to do much work at all but we should still	\
       return a node so that referenced field items below this node	\
       (think proto_item_add_subtree()) will still have somewhere	\
       to attach to or else filtering will not work (they would be	\
       ignored since tree would be NULL).				\
       DON'T try to fake a node where PTREE_FINFO(tree) is NULL	\
       since dissectors that want to do proto_item_set_len() or	\
       other operations that dereference this would crash.		\
       DON'T try to fake a node where PTREE_FINFO(tree) is visible	\
       because that means we can change its length or repr, and we	\
       don't want to do so with calls intended for this faked new	\
       item, so this item needs a new (hidden) child node.		\
       (PROTO_ITEM_IS_HIDDEN(tree) checks both conditions.)		\
       We fake FT_PROTOCOL unless some clients have requested us	\
       not to do so.						\
    */								\
    PTREE_DATA(tree)->count++;					\
    PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo);			\
    if (PTREE_DATA(tree)->count > max_tree_items) {	\
        free_block;						\
        /* Let the exception handler add items to the tree */	\
        PTREE_DATA(tree)->count = 0;				\
    }								\
    if (!(PTREE_DATA(tree)->visible)) {				\
        if (PROTO_ITEM_IS_HIDDEN(tree)) {			\
            if ((hfinfo->ref_type != HF_REF_TYPE_DIRECT)	\
                && (hfinfo->ref_type != HF_REF_TYPE_PRINT)	\
                && (hfinfo->type != FT_PROTOCOL ||		\
                PTREE_DATA(tree)->fake_protocols)) {	\
                free_block;				\
                /* just return tree back to the caller */\
                return tree;				\
            }						\
        }							\
    }

/** See inlined comments.
 @param tree the tree to append this item to
 @param hfindex field index
 @param hfinfo header_field
 @return the header field matching 'hfinfo' */
#define TRY_TO_FAKE_THIS_ITEM(tree, hfindex, hfinfo) \
    TRY_TO_FAKE_THIS_ITEM_OR_FREE(tree, hfindex, hfinfo, ((void)0))


/** See inlined comments.
 @param pi the created protocol item we're about to return */
#define TRY_TO_FAKE_THIS_REPR(pi)	\
	if (!(PTREE_DATA(pi)->visible) && \
	      PROTO_ITEM_IS_HIDDEN(pi)) { \
		/* If the tree (GUI) or item isn't visible it's pointless for \
		 * us to generate the protocol item's string representation */ \
		return pi; \
	}
/* Same as above but returning void */
#define TRY_TO_FAKE_THIS_REPR_VOID(pi)	\
	if (!pi)			\
		return;			\
	if (!(PTREE_DATA(pi)->visible) && \
	      PROTO_ITEM_IS_HIDDEN(pi)) { \
		/* If the tree (GUI) or item isn't visible it's pointless for \
		 * us to generate the protocol item's string representation */ \
		return; \
	}
/* Similar to above, but allows a NULL tree */
#define TRY_TO_FAKE_THIS_REPR_NESTED(pi)	\
	if ((pi == NULL) || (!(PTREE_DATA(pi)->visible) && \
		PROTO_ITEM_IS_HIDDEN(pi))) { \
		/* If the tree (GUI) or item isn't visible it's pointless for \
		 * us to generate the protocol item's string representation */ \
		return pi; \
	}

#ifdef ENABLE_CHECK_FILTER
#define CHECK_HF_VALUE(type, spec, start_values) \
{ \
	const type *current; \
	int n, m; \
	current = start_values; \
	for (n=0; current; n++, current++) { \
		/* Drop out if we reached the end. */ \
		if ((current->value == 0) && (current->strptr == NULL)) { \
			break; \
		} \
		/* Check value against all previous */ \
		for (m=0; m < n; m++) { \
			/* There are lots of duplicates with the same string, \
			   so only report if different... */ \
			if ((start_values[m].value == current->value) && \
			    (strcmp(start_values[m].strptr, current->strptr) != 0)) { \
                printf("Field '%s' (%s) has a conflicting entry in its" \
					  " value_string: %" spec " is at indices %u (%s) and %u (%s)", \
					  hfinfo->name, hfinfo->abbrev, \
					  current->value, m, start_values[m].strptr, n, current->strptr); \
			} \
		} \
	} \
}
#endif

/* The longest NUMBER-like field label we have is for BASE_OUI, which
 * can have up to 64 bytes for the manufacturer name if resolved plus
 * 11 bytes for the "XX:XX:XX ()" part = 75 octets.
 */
#define NUMBER_LABEL_LENGTH 80

#define label_concat(dst, pos, src) \
    label_strcpy(dst, ITEM_LABEL_LENGTH, pos, src, 0)

static void mark_truncated(char *label_str, size_t name_pos, const size_t size);
static void label_mark_truncated(char *label_str, size_t name_pos);
#define LABEL_MARK_TRUNCATED_START(label_str) label_mark_truncated(label_str, 0)


/* Structure for information about a protocol */
struct _protocol {
	const char *name;               /* long description */
	const char *short_name;         /* short description */
	const char *filter_name;        /* name of this protocol in filters */
	GPtrArray  *fields;             /* fields for this protocol */
	int         proto_id;           /* field ID for this protocol */
	bool        is_enabled;         /* true if protocol is enabled */
	bool        enabled_by_default; /* true if protocol is enabled by default */
	bool        can_toggle;         /* true if is_enabled can be changed */
	int         parent_proto_id;    /* Used to identify "pino"s (Protocol In Name Only).
	                                   For dissectors that need a protocol name so they
	                                   can be added to a dissector table, but use the
                                       parent_proto_id for things like enable/disable */
};


/* Contains information about a field when a dissector calls
 * proto_tree_add_item.  */
#define FIELD_INFO_NEW(fi)  fi = wmem_new(field_info)
#define FIELD_INFO_FREE(fi) wmem_free(fi)

/* Contains the space for proto_nodes. */
#define PROTO_NODE_INIT(node)			\
	node->first_child = NULL;		\
	node->last_child = NULL;		\
	node->next = NULL;

#define PROTO_NODE_FREE(node)			\
    wmem_free(node)

/* String space for protocol and field items for the GUI */
#define ITEM_LABEL_NEW(il)			\
    il = wmem_new(item_label_t);
#define ITEM_LABEL_FREE(il)			\
    wmem_free(il);

#define PROTO_REGISTRAR_GET_NTH(hfindex, hfinfo)						\
    if((hfindex == 0 || (unsigned)hfindex > gpa_hfinfo.len)/* && wireshark_abort_on_dissector_bug*/)	\
        printf("Unregistered hf! index=%d", hfindex);					\
    DISSECTOR_ASSERT_HINT(hfindex > 0 && (unsigned)hfindex < gpa_hfinfo.len, "Unregistered hf!");	\
    DISSECTOR_ASSERT_HINT(gpa_hfinfo.hfi[hfindex] != NULL, "Unregistered hf!");	\
    hfinfo = gpa_hfinfo.hfi[hfindex];

